package com.custom.action.core.syncquery;

import com.custom.action.condition.ConditionWrapper;
import com.custom.action.core.TableInfoCache;
import com.custom.action.core.TableParseModel;
import com.custom.action.core.methods.select.SelectListByWrapper;
import com.custom.action.core.methods.select.SelectMapByWrapper;
import com.custom.action.core.methods.select.SelectObjByWrapper;
import com.custom.action.core.methods.select.SelectOneByWrapper;
import com.custom.action.interfaces.ExecuteHandler;
import com.custom.comm.exceptions.CustomCheckException;
import com.custom.comm.utils.CustomUtil;
import com.custom.comm.utils.lambda.LambdaUtil;
import com.custom.comm.utils.lambda.TargetSetter;
import com.custom.jdbc.session.JdbcSqlSessionFactory;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Predicate;

/**
 * @author Xiao-Bai
 * @since 2023/9/13 15:43
 */
@SuppressWarnings("unchecked")
public class SyncResultHandler<T> {

    private final JdbcSqlSessionFactory sqlSessionFactory;

    private final Class<T> target;

    private final List<SyncProperty<T, ?>> syncProperties;

    private SyncResultHandler(JdbcSqlSessionFactory sqlSessionFactory, Class<T> target, List<SyncProperty<T, ?>> syncProperties) {
        this.sqlSessionFactory = sqlSessionFactory;
        this.target = target;
        this.syncProperties = syncProperties;
    }

    public static <T> SyncResultHandler<T> create(JdbcSqlSessionFactory sqlSessionFactory, Class<T> target, List<SyncProperty<T, ?>> syncProperties) {
        return new SyncResultHandler<>(sqlSessionFactory, target, syncProperties);
    }


    /**
     * 处理属性值的查询
     */
    public void doHandler(T currObj) throws Exception {
        for (SyncProperty<T, ?> property : syncProperties) {
            Predicate<T> ifCondition = property.getCondition();
            if (ifCondition != null && ifCondition.test(currObj)) {
                this.doSyncQuery(currObj, property);
            }
        }
    }


    /**
     * 同步查询当前属性的结果
     */
    private <P> void doSyncQuery(T data, SyncProperty<T, ?> property) throws Exception {
        TargetSetter<T, P> setter = (TargetSetter<T, P>) property.getSetter();
        ConditionWrapper<?> wrapper = property.getWrapper();
        SyncFunction<T, ?> syncFunction = property.getSyncFunction();
        if (setter == null || (wrapper == null && syncFunction == null)) {
            return;
        }
        if (syncFunction != null) {
            wrapper = syncFunction.doQuery(data);
        }

        TableParseModel<?> tableModel = TableInfoCache.getTableModel(target);
        String implMethodName = LambdaUtil.getImplMethodName(setter);

        Method setMethod = tableModel.getPropertyList().stream().map(PropertyDescriptor::getWriteMethod)
                .filter(op -> op.getName().equals(implMethodName))
                .findFirst()
                .orElseThrow(() -> new CustomCheckException("Parse setter method error: " + implMethodName + " in " + target));

        // 获取该属性setter 方法的参数类型
        Class<?> fieldType = setMethod.getParameterTypes()[0];
        if (fieldType.isArray() || fieldType.isEnum()) {
            throw new UnsupportedOperationException("Injection methods with attribute type of (array/enum) are not currently supported.");
        }
        // 设置属性结果
        this.doSetPropertyResult(data, wrapper, fieldType, setter);
    }


    /**
     * 设置属性的结果
     */
    private <P> void doSetPropertyResult(T data, ConditionWrapper<?> wrapper,
                                            Class<?> fieldType, TargetSetter<T, P> setter) throws Exception {
        ExecuteHandler executeHandler = null;
        Object[] preParamArr = {wrapper};
        if (Collection.class.isAssignableFrom(fieldType)) {
            executeHandler = new SelectListByWrapper();
        } else if (Map.class.isAssignableFrom(fieldType)) {
            executeHandler = new SelectMapByWrapper();
        } else if (CustomUtil.isBasicClass(fieldType)) {
            executeHandler = new SelectObjByWrapper();
        } else {
            executeHandler = new SelectOneByWrapper();
        }

        Class<T> mappedType = executeHandler.getMappedType(preParamArr);
        P result = (P) executeHandler.startExecute(sqlSessionFactory, mappedType, preParamArr);

        if (Set.class.isAssignableFrom(fieldType)) {
            result = (P) new HashSet<>((Collection<T>) result);
        }

        if (result != null) {
            setter.accept(data, result);
        }
    }



}
